package com.baizhi.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.baizhi.annotation.GetCacheAnnotation;
import com.baizhi.dao.CmfzUserDao;
import com.baizhi.entity.CmfzUser;
import com.baizhi.service.CmfzUserService;
import com.baizhi.util.GenerateUserUtil;
import com.baizhi.util.ThreadPollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;

@Service
@Transactional
public class CmfzUserServiceImpl implements CmfzUserService {
    //创建日志对象
    //注意：Logger和LoggerFactory都是org.slf4j包下的
    //      getLogger（）方法的参数是当前类的类对象
    private static Logger logger = LoggerFactory.getLogger(CmfzUserServiceImpl.class);
    @Autowired
    private CmfzUserDao cmfzUserDao;
    @Override
    public void updateCmfzUser(Integer userId) {
        //使用日志打印信息  一般使用info和debug两个级别
        logger.debug("用户业务层更新方法的参数："+userId);
        logger.info("用户业务层更新方法的参数："+userId);
        //1、先获得要修改的用户对象
        CmfzUser cmfzUser = cmfzUserDao.selectById(userId);
        //修改状态
        if(cmfzUser.getUserStatus()==0){
            cmfzUser.setUserStatus(1);
        }else {
            cmfzUser.setUserStatus(0);
        }
        //3、修改操作
        cmfzUserDao.updateById(cmfzUser);
    }

    @Override
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public Page<CmfzUser> selectPage(String userTelphone, Integer page, Integer limit) {
        Page<CmfzUser> cmfzUserPage = null;
        Page<CmfzUser> userPage = new Page<>(page, limit);
        if(userTelphone!=null){
            QueryWrapper<CmfzUser> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("user_telphone",userTelphone);
            cmfzUserPage = cmfzUserDao.selectPage(userPage, queryWrapper);
        }else{
            cmfzUserPage = cmfzUserDao.selectPage(userPage,null);
        }
        return cmfzUserPage;
    }

    @Override
    //查询出用户表中男女人数  封装到一个集合中  在页面显示为一个柱状图
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public List<Integer> selectBoyAndGril() {
        Integer count = cmfzUserDao.selectCount(null);
        QueryWrapper<CmfzUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_sex","男");
        Integer boyNum = cmfzUserDao.selectCount(queryWrapper);
        Integer girlNum = count-boyNum;
        List<Integer> list = new ArrayList<>();
        list.add(boyNum);
        list.add(girlNum);
        return list;
    }

    @Override
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public List<Map> selectByProvince() {
        List<Map> maps = cmfzUserDao.selectByProvince();
        return maps;
    }

    @Override
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public List<Integer> selectByDateWeek() {
//    查询从当前时间往前n周的一周之内的注册量  即
//    n=1     1~7 天之内的注册人数
//    n=2    7~14 天之内的注册人数
//    n=3    14~21 天之内的注册人数
        Integer size1 = cmfzUserDao.selectByDate(1);//1-7天之前的注册人数
        Integer size2 = cmfzUserDao.selectByDate(2);//7-14天之前的注册人数
        Integer size3 = cmfzUserDao.selectByDate(3);//21-14天之前的注册人数
        ArrayList<Integer> list = new ArrayList<>();
        list.add(size1);
        list.add(size2);
        list.add(size3);
        return list;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Integer> selectByDateWeek2() {
        return cmfzUserDao.selectByDate2();
    }

    /*
    * 多线程查询  一个方法封装上面三个图表查询（注册量、男女比例、各省份用户量）
    * */
    @Override
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public Map selectCountByThread() {
        logger.info("用户业务层整合三张图表的查询方法---线程优化");
        Map map = new HashMap();
        //1、创建一个计数器类对象  用来标注三个线程是否执行完  只有子线程都结束 才允许主线程返回结果
        CountDownLatch countDownLatch = new CountDownLatch(3);//这个参数3 就是此次的任务数量
        //2、利用自己写的工具类ThreadPollUtil 获取线程池对象ThreadPoolExecutor  里面就封装了很多线程
        // 直接拿来用即可 线程就是用来提交线程任务的
        ThreadPoolExecutor executor = ThreadPollUtil.executor;
        //2.1、利用匿名内部类  实现Runnable接口  创建三个线程任务 并交给线程对象执行
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程1  用来查询男女比例
                ArrayList<Integer> list = new ArrayList<>();

                List<Map> maps = cmfzUserDao.selectCountBySex();
                //上面方法返回的list集合中是map  并且k和v都是object而出类型
                //这个map里的key分别是sex 和value   我们只需要value对应的值
                for (Map map : maps) {
                    list.add(Integer.parseInt(map.get("value").toString()));
                }
                map.put("sex",list);
                //任务结束  计数器中任务量减1
                countDownLatch.countDown();
            }
        });
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程2  每周注册量的查询
                List<Integer> list = cmfzUserDao.selectByDate2();
                map.put("count",list);
                //任务结束 计数器减1
                countDownLatch.countDown();
            }
        });
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程3 各省份用户量的查询
                List<Map> maps = cmfzUserDao.selectByProvince();
                //这个返回的list集合泛型就是map类型  因为前台页面展示中国地图时就需要这样的数据 所以不用转
                map.put("province",maps);
                //任务结束 计数器减1
                countDownLatch.countDown();
            }
        });
        //3、利用计数器类的方法  阻塞主线程  只有计数器中任务量为0时  才继续主线程
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //4、返回结果
        return map;
    }

    /*
    * //多线程查询  一个方法封装上面三个图表查询（注册量、男女比例、各省份用户量）
    * */
    @Override
    @Transactional(readOnly = true)
    @GetCacheAnnotation//将该方法查询的数据缓存到redis中
    public Map selectCountByThread2() {
        Map map = new HashMap();
        //1、创建一个计数器类对象  用来标注三个线程是否执行完  只有子线程都结束 才允许主线程返回结果
        CountDownLatch countDownLatch = new CountDownLatch(3);//这个参数3 就是此次的任务数量
        //2、利用自己写的工具类ThreadPollUtil 获取线程池对象ThreadPoolExecutor  里面就封装了很多线程
        // 直接拿来用即可 线程就是用来提交线程任务的
        ThreadPoolExecutor executor = ThreadPollUtil.executor;
        //2.1、利用匿名内部类  实现Runnable接口  创建三个线程任务 并交给线程对象执行
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程1  用来查询男女比例
                List<Map> maps = cmfzUserDao.selectCountBySex();
                map.put("sex",maps);
                //任务结束  计数器中任务量减1
                countDownLatch.countDown();
            }
        });
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程2  每周注册量的查询
                List<Map> maps = cmfzUserDao.selectCountByDate();
                map.put("count",maps);
                //任务结束 计数器减1
                countDownLatch.countDown();
            }
        });
        executor.submit(new Runnable() {
            @Override
            public void run() {
                //线程3 各省份用户量的查询
                List<Map> maps = cmfzUserDao.selectByProvince();
                //这个返回的list集合泛型就是map类型  因为前台页面展示中国地图时就需要这样的数据 所以不用转
                map.put("province",maps);
                //任务结束 计数器减1
                countDownLatch.countDown();
            }
        });
        //3、利用计数器类的方法  阻塞主线程  只有计数器中任务量为0时  才继续主线程
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("使用线程优化整合三张图表的查询方法的结果："+map);
        //4、返回结果
        return map;
    }
}
